#ifndef __M_THRPOOL_H__
#define __M_THRPOOL_H__
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include <memory>
#include <vector>
#include <functional>
#include <mutex>
#include <condition_variable>

class threadpool
{
public:
    using ptr = std::shared_ptr<threadpool>;
    using Functor = std::function<void(void)>;
    threadpool(int thr_num = 1):_stop(false)
    {
        for(int i = 0;i<thr_num;i++)
        {
            _threads.emplace_back(&threadpool::entry,this);
        }
    }

    ~threadpool()
    {
        stop();
    }

    void stop()
    {
        if(_stop == true) return;
        _stop = true;
        _condition.notify_all();
        for(auto &thr : _threads)
        {
            thr.join();
        }
    }

    // 外面将任务函数传进来，会有很多种不同的参数，我们不知道参数具体是什么样子的，所以就采用不定参和返回值
    // 在push函数中，将传入的函数封装成一个异步任务(packaged_task)
    // 使用lambda生成一个可调用对象（内部执行异步任务），抛入到任务池中，由工作线程取出进行执行
    template<typename F,typename ...Args>
    auto push(F &&func,Args&& ...args) -> std::future<decltype(func(args...))>
    {
        // 1. 在这将传递的参数进行包装，封装成一个packaged_task任务
            // 获取任务的返回类型
            using return_type = decltype(func(args...));
            // 用std::bind把"函数func"和“它的参数args...”绑在一起，生成一个“无参数的可调用对象”
            // 比如func是Add，args是1和2，tmp_func就是一个“不用传参，调用就执行Add(1,2)”的对象
            auto tmp_func = std::bind(std::forward<F>(func),std::forward<Args>(args)...);
            // 用std::packaged_task包装tmp_func（packaged_task是“带结果的任务包装器”）
            // 模板参数return_type()表示：这个任务无参数，返回return_type类型
            // 用shared_ptr是因为后面要在lambda里捕获它，确保任务执行时它不会被销毁
            auto task = std::make_shared<std::packaged_task<return_type()>>(tmp_func);
            // 从packaged_task中获取future（未来结果的“凭证”），后续用户用它拿结果
            std::future<return_type> fu = task->get_future();
        // 2. 构造一个lambda匿名函数（捕获任务对象），函数内执行任务对象
        {
            // 加锁：用unique_lock锁定互斥量_mutex，确保接下来对_taskpool的操作是线程安全的
            std::unique_lock<std::mutex> lock(_mutex);
            // 把任务放进队列：构造一个lambda，里面执行packaged_task（即执行用户的函数）
            // lambda捕获task（shared_ptr，确保task在lambda执行时还活着）
            // 这个lambda是“无参数的可调用对象”，正好符合任务队列的要求（比如之前的Functor类型）
            _task.push_back([task](){ (*task)(); });

            // 通知一个等待的工作线程：“有新任务了，快来执行！”
            _condition.notify_one();
        }
        return fu;
    }


private:
    void entry()
    {
        while(!_stop)
        {
            std::vector<Functor> tmp_taskpool;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _condition.wait(lock,[this](){return _stop || !_task.empty();});
                tmp_taskpool.swap(_task);
            }
            for(auto& task : tmp_taskpool)
            {
                task();
            }
        }
    }
private:
    std::atomic<bool> _stop; // 是否运行
    std::mutex _mutex; // 保证同步关系
    std::condition_variable _condition;
    std::vector<std::thread> _threads; // 工人
    std::vector<Functor> _task; // 任务
};  
#endif